package com.moon.interview.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moon.interview.exception.ApplicationException;
import com.moon.interview.exception.SysErrorCode;
import com.moon.interview.mapper.QuestionMapper;
import com.moon.interview.model.dto.question.QuestionAddRequest;
import com.moon.interview.model.dto.question.QuestionQueryRequest;
import com.moon.interview.model.dto.question.QuestionUpdateRequest;
import com.moon.interview.model.entity.Question;
import com.moon.interview.model.entity.QuestionBankQuestion;
import com.moon.interview.model.entity.User;
import com.moon.interview.model.vo.QuestionSimpleVo;
import com.moon.interview.model.vo.QuestionVo;
import com.moon.interview.service.IQuestionBankQuestionService;
import com.moon.interview.service.IQuestionService;
import com.moon.interview.service.IUserService;
import com.moon.interview.utils.ThrowUtils;
import com.moon.interview.utils.UserUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper,Question> implements IQuestionService {

    @Resource
    private IUserService userService;

    @Resource
    private IQuestionBankQuestionService questionBankQuestionService;

    /**
     * 组装查询条件
     * @param queryRequest  查询参数
     * @return wrapper
     */
    private LambdaQueryWrapper<Question> accessibleQueryWrapper(QuestionQueryRequest queryRequest) {
        LambdaQueryWrapper<Question> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(queryRequest.getUserId() != null, Question::getUserId, queryRequest.getUserId());
        // 搜索文本
        if (StringUtils.isNotBlank(queryRequest.getTextSearch())) {
            wrapper.like(Question::getTitle, queryRequest.getTextSearch())
                    .or().like(Question::getContent, queryRequest.getTextSearch())
                    .or().like(Question::getAnswer, queryRequest.getTextSearch());
        }
        // 题库ID不为空时 查询关联的题目IDList
        if (queryRequest.getBankId() != null) {
            wrapper.in(Question::getId, questionBankQuestionService.getBaseMapper().selectList(
                    Wrappers.<QuestionBankQuestion>lambdaQuery().eq(QuestionBankQuestion::getQuestionBankId, queryRequest.getBankId())));
        }
        wrapper.last(queryRequest.getOrderSql()); // 排序

        return wrapper;
    }

    /**
     * 获取QuestionVo
     * @param question 题目
     * @return QuestionVo
     */
    public QuestionVo getQuestionVo(Question question) {
        if (question == null) {
            return null;
        }
        QuestionVo questionVo = new QuestionVo();
        BeanUtils.copyProperties(question, questionVo);
        questionVo.setTagList(question.transTagList(question.getTags()));
        // 获取用户信息
        User user = userService.getById(question.getUserId());
        questionVo.setUserName(user != null ? user.getUserName():"");
        return questionVo;
    }


    @Override
    public Page<QuestionVo> pageQuestionVo(QuestionQueryRequest queryRequest) {
        LambdaQueryWrapper<Question> wrapper = accessibleQueryWrapper(queryRequest);
        Page<Question> page = this.page(new Page<>(queryRequest.getPageNum(), queryRequest.getPageSize()), wrapper);
        // 组装vo
        List<QuestionVo> questionVoList = new ArrayList<>();
        for (Question question : page.getRecords()) {
            QuestionVo questionVo = getQuestionVo(question);
            questionVoList.add(questionVo);
        }
        Page<QuestionVo> pageVo = new Page<>(page.getCurrent(),page.getSize(),page.getTotal());
        pageVo.setRecords(questionVoList);
        return pageVo;
    }

    @Override
    public Page<QuestionVo> pageQuestionVoByLoginUser(QuestionQueryRequest queryRequest) {
        // 获取登录用户信息
        User user = UserUtils.getLoginUser();
        queryRequest.setUserId(user.getId());
        return pageQuestionVo(queryRequest);
    }

    @Override
    public Page<QuestionSimpleVo> pageQuestionSimpleVo(QuestionQueryRequest queryRequest) {
        LambdaQueryWrapper<Question> wrapper = accessibleQueryWrapper(queryRequest);
        // 题库ID不为空时 查询关联的题目IDList
        if (queryRequest.getBankId() != null) {
            List<QuestionBankQuestion> questionBankQuestionList = questionBankQuestionService.getBaseMapper().selectList(
                    Wrappers.<QuestionBankQuestion>lambdaQuery().eq(QuestionBankQuestion::getQuestionBankId, queryRequest.getBankId()));
            if (CollUtil.isEmpty(questionBankQuestionList)) {
                return null;
            }
            List<Long> questionIdList = questionBankQuestionList.stream().map(li -> li.getQuestionId()).collect(Collectors.toList());
            wrapper.in(Question::getId, questionIdList);
        }
        Page<Question> page = this.page(new Page<>(queryRequest.getPageNum(), queryRequest.getPageSize()), wrapper);
        Page<QuestionSimpleVo> pageVo = new Page<>(page.getCurrent(),page.getSize(),page.getTotal());
        List<QuestionSimpleVo> questionListVoList = new ArrayList<>();
        for(Question question : page.getRecords()) {
            QuestionSimpleVo questionListVo = new QuestionSimpleVo();
            questionListVo.setId(question.getId());
            questionListVo.setTitle(question.getTitle());
            questionListVo.setTagList(question.transTagList(question.getTags()));
            questionListVoList.add(questionListVo);
        }
        pageVo.setRecords(questionListVoList);
        return pageVo;
    }

    @Override
    public QuestionVo getQuestionVo(Long id) {
        Question question = this.getById(id);
        return getQuestionVo(question);
    }

    @Override
    public Boolean addQuestion(QuestionAddRequest addRequest) {
        // 获取当前登录信息
        User user = UserUtils.getLoginUser();
        Question question = new Question();
        BeanUtils.copyProperties(addRequest, question);
        question.setUserId(user.getId());
        String tagStr = question.transTagStr(addRequest.getTagList());
        question.setTags(tagStr);
        boolean flag = this.save(question);
        if(!flag) {
            throw new ApplicationException(SysErrorCode.OPERATION_ERROR,"新增失败");
        }
        return true;
    }

    @Override
    public Boolean updateQuestion(QuestionUpdateRequest updateRequest) {
        Question question = new Question();
        BeanUtils.copyProperties(updateRequest, question);
        String tagStr = question.transTagStr(updateRequest.getTagList());
        question.setTags(tagStr);
        boolean flag = this.updateById(question);
        if(!flag) {
            throw new ApplicationException(SysErrorCode.OPERATION_ERROR,"更新失败");
        }
        return true;
    }

    @Override
    public Boolean removeQuestion(Long id) {
        boolean flag = this.removeById(id);
        ThrowUtils.throwIf(!flag, SysErrorCode.OPERATION_ERROR,"删除题目失败");
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)  // 开启事务
    public Boolean removeBatchQuestion(List<Long> idList) {
        if (CollUtil.isEmpty(idList)) {
            throw new ApplicationException(SysErrorCode.PARAMS_ERROR, "要删除的题目列表不能为空");
        }
        // todo 后期优化  数据量过大  记录每个失败的id进行重发  重发3次不再进行重发，记录到错误日志中。
        boolean flag = this.removeBatchByIds(idList);  // 删除题目
        ThrowUtils.throwIf(!flag, SysErrorCode.OPERATION_ERROR,"删除题目失败");
        // 删除题目题库关联信息
        flag = questionBankQuestionService.remove(Wrappers.<QuestionBankQuestion>lambdaUpdate().in(QuestionBankQuestion::getQuestionId, idList));
        ThrowUtils.throwIf(!flag, SysErrorCode.OPERATION_ERROR,"删除题目题库关联失败");
        return true;
    }



}
